﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Net;
using System.ServiceModel;
using BookTheBook.BooksFromAmazonImporter.AmazonCommerceService;
using BookTheBook.Data.Model;

namespace BookTheBook.Amazon
{
    public class AmazonBookEntitiesProvider : IBookEntitiesProvider
    {
        private static int _keywordsCount;

        public int GetBooksForGivenKeywords(List<string> keywords, int pageIndex, out List<Book> searchResults)
        {
            //Props to Oren Trutner, Erica Sanders, Jason Foglia and Andrew
            BasicHttpBinding basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport) { MaxReceivedMessageSize = int.MaxValue };
            basicHttpBinding.ReaderQuotas.MaxStringContentLength = 32768;
            AWSECommerceServicePortTypeClient amazonClient =
                new AWSECommerceServicePortTypeClient(
                    basicHttpBinding,
                    new EndpointAddress("https://webservices.amazon.com/onca/soap?Service=AWSECommerceService")
                    );
            amazonClient.ChannelFactory.Endpoint.Behaviors.Add(new AmazonSigningEndpointBehavior());

            List<ItemSearchRequest> itemSearchRequests = new List<ItemSearchRequest>();

            _keywordsCount = keywords.Count;
            for (int i = 0; i < _keywordsCount; i++)
            {
                itemSearchRequests.Add(new ItemSearchRequest
                                           {
                                               ItemPage = pageIndex.ToString(),
                                               SearchIndex = "Books",
                                               Keywords = keywords[i],
                                               ResponseGroup = new[] { "ItemAttributes", "Large", "EditorialReview", "Reviews" }
                                           });
            }

            List<ItemSearchResponse> responses = new List<ItemSearchResponse>(_keywordsCount);

            List<ItemSearch> itemSearches = new List<ItemSearch>();
            for (int i = 0; i < _keywordsCount; i++)
            {
                itemSearches.Add(new ItemSearch
                                     {
                                         Request = new[] { itemSearchRequests[i] },
                                         AWSAccessKeyId = ConfigurationManager.AppSettings["AWSAccessKeyId"]
                                     });
            }

            for (int i = 0; i < _keywordsCount; i++)
            {
                responses.Add(amazonClient.ItemSearch(itemSearches[i]));
            }

            List<Book> bookEntities = new List<Book>();
            
            ItemSearchResponse item = responses[0];

            List<Item[]> items = new List<Item[]>();
            for (int i = 0; i < _keywordsCount; i++)
            {
                items.Add(responses[i].Items[0].Item);
            }

            foreach (Item[] itemSet in items)
            {
                foreach (Item bookFound in itemSet)
                {
                    if (bookFound.ItemAttributes != null)
                    {
                        bookEntities.Add(new Book
                                             {
                                                 ISBN = bookFound.ItemAttributes.ISBN ?? string.Empty,
                                                 Title = bookFound.ItemAttributes.Title,
                                                 Author =
                                                     bookFound.ItemAttributes.Author != null
                                                         ? String.Join(", ", bookFound.ItemAttributes.Author)
                                                         : "-",
                                                 Publisher = bookFound.ItemAttributes.Manufacturer ?? "-",
                                                 Edition = bookFound.ItemAttributes.Edition ?? "-",
                                                 Rating = GetCustomerRaiting(bookFound.CustomerReviews),
                                                 DetailsUrl = bookFound.DetailPageURL,
                                                 ImageUrl =
                                                     bookFound.MediumImage != null ? bookFound.MediumImage.URL : "NoUrl",
                                                 Price =
                                                     GetPriceWithoutCurrencySymbol(bookFound.ItemAttributes.ListPrice),
                                                 Currency = GetCurrencySymbol(bookFound.ItemAttributes.ListPrice),
                                                 Image = GetImageBytes(bookFound.MediumImage),
                                                 PublishYear = GetPublishDate(bookFound.ItemAttributes.PublicationDate),
                                                 Description = bookFound.ItemAttributes.ShortSynopsis
                                             });
                    }
                }
            }

            searchResults = bookEntities;

            int totalResults;
            int.TryParse(item.Items[0].TotalResults, out totalResults);
            if (totalResults < 1)
            {
                totalResults = item.Items[0].Item.Length;
                if (totalResults < 1)
                {
                    return 0;
                }
            }

            int totalPages;
            int.TryParse(item.Items[0].TotalPages, out totalPages);
            if (totalPages < 1)
            {
                totalPages = 1;
            }

            return totalPages;

            //TODO: Refactor Main() to be more OO, add IoC - DI, remove magic strings
            //TODO: Refactor to read AWSAccessKeyId only once from config
        }

        private static decimal GetCustomerRaiting(CustomerReviews customerReviews)
        {
            if (customerReviews != null) return customerReviews.AverageRating;
            return decimal.MinusOne;
        }

        private static byte[] GetImageBytes(Image mediumImage)
        {
            if (mediumImage == null || mediumImage.URL == null) return new byte[10];
            WebRequest webRequest = WebRequest.Create(new Uri(mediumImage.URL));
            WebResponse webResponse = webRequest.GetResponse();
            Stream stream = webResponse.GetResponseStream();

            using (BinaryReader binaryReader = new BinaryReader(stream))
            {
                return binaryReader.ReadBytes(500000);
            }
        }

        private static string GetCurrencySymbol(Price listPrice)
        {
            if (listPrice == null)
                return "?";
            return listPrice.FormattedPrice.Substring(0, 1);
        }

        private static decimal GetPriceWithoutCurrencySymbol(Price listPrice)
        {
            if (listPrice == null)
                return 0;
            decimal price;
            decimal.TryParse(listPrice.FormattedPrice.TrimStart(new[] { '$' }), NumberStyles.Any, new NumberFormatInfo(), out price);
            return price;
        }

        private static DateTime GetPublishDate(string publicationDateToParse)
        {
            DateTime publicationDate;
            DateTime.TryParse(publicationDateToParse, DateTimeFormatInfo.InvariantInfo,
                              DateTimeStyles.AssumeUniversal, out publicationDate);
            if (IsNotAValidSqlDate(publicationDate)) { return new DateTime(1899, 1, 1, 1, 1, 1);}
            return publicationDate;
        }

        private static bool IsNotAValidSqlDate(DateTime publicationDate)
        {
            if ((new DateTime(1900, 1, 1) < publicationDate) && publicationDate < new DateTime(9999,1,1))
            {
                return false;    
            }
            return true;
        }
    }
}